package org.ytor.core.sqlflow.config;

import net.bytebuddy.implementation.bind.annotation.*;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.ytor.common.classmeta.ClassMetadata;
import org.ytor.common.classmeta.MethodMetadata;
import org.ytor.common.classmeta.ParameterMetadata;
import org.ytor.common.exception.BaseException;
import org.ytor.common.util.Colls;
import org.ytor.common.util.cache.ClassCache;
import org.ytor.common.util.convert.Converts;
import org.ytor.core.sqlflow.dsl.DslCache;
import org.ytor.core.sqlflow.dsl.dsqlfile.DsqlSegment;
import org.ytor.core.sqlflow.dsl.template.RenderResult;
import org.ytor.core.sqlflow.enums.SqlType;
import org.ytor.core.sqlflow.executor.DMLResult;
import org.ytor.core.sqlflow.executor.DQLResult;
import org.ytor.core.sqlflow.executor.SQLRuntime;
import org.ytor.core.sqlflow.log.SqlLogHelper;
import org.ytor.core.sqlflow.plugin.Interceptor;
import org.ytor.core.sqlflow.plugin.InterceptorManager;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * created by yangtong on 2025/8/14 09:49:28
 * <br/>
 * 抽象方法拦截器
 */
public class SqlMethodInterceptor {
    private final AutowireCapableBeanFactory factory;

    public SqlMethodInterceptor(AutowireCapableBeanFactory factory) {
        this.factory = factory;
    }

    /**
     * 抽象方法统一走的代理
     */
    @RuntimeType
    public Object intercept(@Origin Method targetMethod,  // 目标方法
                            @Origin Class<?> targetClass, // 目标class
                            @AllArguments Object[] args,    // 方法参数值
                            @This Object proxy,     // 代理对象
                            @SuperMethod(nullIfImpossible = true) Method superMethod) {
        // 获取目标类的类元缓存
        ClassMetadata<?> classMetadata = ClassCache.get(targetClass);

        // 根据全类名和方法名称生成的id，得到自定义的SQL段落
        String id = targetClass.getName() + "." + targetMethod.getName();
        DsqlSegment segment = DslCache.get(id);
        if (segment == null) {
            throw new BaseException("根据【{}】没有找到SQL映射", id);
        }

        // 获取原本的方法元数据
        List<MethodMetadata> methods = classMetadata.getMethods(m -> m.getOriginMethod().equals(targetMethod));
        if (Colls.isEmpty(methods)) {
            throw new BaseException("未找到目标方法【{}】，无法调用，请检查代码", targetMethod.getName());
        }
        MethodMetadata methodMetadata = methods.getFirst();

        // 解析参数
        Map<String, Object> params = new HashMap<>();
        for (ParameterMetadata parameter : methodMetadata.parameters()) {
            params.put(parameter.getName(), args[parameter.getIndex()]);
        }

        // 获取目标方法返回类型
        Class<?> returnType = methodMetadata.returnType();
        // 根据参数渲染SQL
        RenderResult result = segment.parse(params);

        // 将SQL交给数据库执行
        // 返回查询结果
        if (result.sqlType() == SqlType.SELECT) {
            SqlLogHelper.info("开始构建自定义 SELECT SQL");
            DQLResult DQLResult = doSelect(result.finalString(), result.params());

            // 方法返回值是集合或数组类型
            if (methodMetadata.isAggregateReturnType()) {
                // 获取聚合元素
                Class<?> elementType = methodMetadata.getElementType();
                return DQLResult.toBeans(elementType);
            }
            // 普通实体类
            else {
                return DQLResult.toBean(returnType);
            }
        }
        // 返回增删改的影响行数
        else {
            SqlLogHelper.info("开始构建自定义 {} SQL", result.sqlType().name());
            DMLResult dmlResult;
            if (result.sqlType() == SqlType.INSERT) {
                dmlResult = doInsert(result.finalString(), result.params());
            } else if (result.sqlType() == SqlType.DELETE) {
                dmlResult = doDelete(result.finalString(), result.params());
            } else if (result.sqlType() == SqlType.UPDATE) {
                dmlResult = doUpdate(result.finalString(), result.params());
            } else {
                throw new IllegalArgumentException("未知的SQL 类型: " + result.sqlType());
            }

            // 如果方法返回值是void，直接返回
            if (returnType == void.class || returnType == Void.class) {
                return null;
            }
            // 如果方法返回值是布尔类型，判断增删改操作是否成功
            else if (returnType == boolean.class || returnType == Boolean.class) {
                return dmlResult.getRowAffected() > 0;
            }
            // 如果方法返回值是其他类型，则进行类型转换
            else {
                return Converts.convert(dmlResult.getRowAffected(), returnType);
            }
        }
    }

    private DQLResult doSelect(String sql, List<Object> params) {
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.beforeSelect(null, SqlType.parseTable(sql), sql, null, null, params);
        }

        DQLResult result = SQLRuntime.doSelect(sql, params);

        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.afterSelect(null, SqlType.parseTable(sql), sql, null, null, params, result);
        }

        return result;
    }

    private DMLResult doInsert(String sql, List<Object> params) {
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.beforeInsert(null, SqlType.parseTable(sql), sql, params);
        }

        DMLResult result = SQLRuntime.doInsert(sql, params);

        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.afterInsert(null, SqlType.parseTable(sql), sql, params, result);
        }

        return result;
    }

    private DMLResult doDelete(String sql, List<Object> params) {
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.beforeDelete(null, SqlType.parseTable(sql), sql, null, params);
        }

        DMLResult result = SQLRuntime.doInsert(sql, params);

        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.afterDelete(null, SqlType.parseTable(sql), sql, null, params, result);
        }

        return result;
    }

    private DMLResult doUpdate(String sql, List<Object> params) {
        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.beforeUpdate(null, SqlType.parseTable(sql), sql, null, params);
        }

        DMLResult result = SQLRuntime.doInsert(sql, params);

        for (Interceptor interceptor : InterceptorManager.interceptors()) {
            interceptor.afterUpdate(null, SqlType.parseTable(sql), sql, null, params, result);
        }

        return result;
    }
}
